package tinf.entropijsko.binarno;

import java.util.ArrayList;

import tinf.entropijsko.UredjenaTrojka;

/**
 * Metode za pretvorbu u binarne i iz binarnih reprezentacija trojki
 * 
 * @author Dado
 */
public class LZ77Binarno {

	public int tripletBinarySize = 9;
	private String A = "000";
	private String B = "001";
	private String C = "010";
	private String D = "011";
	private String E = "100";
	private String END = "101";

	/**
	 * Pretvara listu uređenih trojki u binarni zapis
	 * 
	 * @param trojke
	 *            Lista uređenih trojki
	 * @return Binarna prezentacija liste trojki
	 */
	public String pretvoriTrojkeUBinarno(ArrayList<UredjenaTrojka> trojke) {

		StringBuilder binarno = new StringBuilder();

		for (UredjenaTrojka triplet : trojke) {
			binarno.append(brojUBinarno(triplet.getPomak()));
			binarno.append(brojUBinarno(triplet.getDuljina()));
			binarno.append(simbolUBinarno(triplet.getSljedeciSimbol()));
		}

		return binarno.toString();
	}

	/**
	 * Pretvara binarni zapis trojke u objekt tipa UredjenaTrojka.<br>
	 * (pomak, duljina, sljedeciSimbol)
	 * 
	 * @param binarnaTrojka
	 *            binarni zapis trojke (9 bitova)
	 * @return nova UredjenaTrojka
	 */
	public UredjenaTrojka pretvoriBinarnoUTrojku(String binarnaTrojka) {

		if (binarnaTrojka.length() != tripletBinarySize) {
			System.out
					.println("Neispravna duljina binarnog zapisa trojke! Mora biti duljine 9!");
			return null;
		}

		String pomakBin = binarnaTrojka.substring(0, 3);
		String duljinaBin = binarnaTrojka.substring(3, 6);
		String sljedeciSimbolBin = binarnaTrojka.substring(6);

		int pomak = binarnoUBroj(pomakBin);
		int duljina = binarnoUBroj(duljinaBin);
		Character sljedeciSimbol = binarnoUSimbol(sljedeciSimbolBin);

		return new UredjenaTrojka(pomak, duljina, sljedeciSimbol);
	}

	/**
	 * Pretvara binarni niz u niz uređenih trojki prikladnih za LZ77 dekoder.
	 * 
	 * @param binarneTrojke
	 *            niz binarnog zapisa trojki
	 * @return listu uređenih trojki
	 */
	public ArrayList<UredjenaTrojka> pretvoriNizUTrojke(String binarneTrojke) {
		if (binarneTrojke.length() % tripletBinarySize != 0) {
			System.out
					.println("Neispravna duljina binarnog zapisa trojki (mora biti visekratnik 9).");
			return null;
		}

		int brojTrojki = binarneTrojke.length() / 9;
		ArrayList<UredjenaTrojka> uredjeneTrojke = new ArrayList<UredjenaTrojka>();
		for (int i = 0; i < brojTrojki; i++) {
			uredjeneTrojke.add(pretvoriBinarnoUTrojku(binarneTrojke.substring(i
					* tripletBinarySize, (i + 1) * tripletBinarySize)));
		}

		return uredjeneTrojke;
	}

	/**
	 * Pretvara cijeli broj u binarni zapis (3 znamenke)
	 * 
	 * @param num
	 *            Broj koji treba pretvoriti
	 * @return Binarni zapis
	 */
	public String brojUBinarno(int num) {

		String binarno = Integer.toBinaryString(num);
		StringBuilder builder = new StringBuilder();

		// mora biti duljine 3 bita
		for (int i = binarno.length(); i < 3; i++) {
			builder.append('0');
		}
		builder.append(binarno);

		return builder.toString();
	}

	/**
	 * Pretvara binarni zapis (String) u dekadski cijeli broj
	 * 
	 * @param bin
	 *            String binarnog zapisa
	 * @return Dekadski cijeli broj
	 */
	public int binarnoUBroj(String bin) {
		return Integer.parseInt(bin, 2);
	}

	/**
	 * Pretvara simbol (a,b,c,d,e,*) u binarni zapis (3 znamenke)
	 * 
	 * @param simbol
	 *            Simbol koji treba pretvoriti
	 * @return Binarni zapis
	 */
	public String simbolUBinarno(Character simbol) {
		if (simbol.equals('a')) {
			return A;
		} else if (simbol.equals('b')) {
			return B;
		} else if (simbol.equals('c')) {
			return C;
		} else if (simbol.equals('d')) {
			return D;
		} else if (simbol.equals('e')) {
			return E;
		} else if (simbol.equals('*')) {
			return END;
		}
		return null;
	}

	/**
	 * Pretvara binarni zapis (String) u odgovarajući simbol
	 * 
	 * @param bin
	 *            String binarnog zapisa
	 * @return Dekodirani simbol
	 */
	public Character binarnoUSimbol(String bin) {
		if (bin.equals(A)) {
			return 'a';
		} else if (bin.equals(B)) {
			return 'b';
		} else if (bin.equals(C)) {
			return 'c';
		} else if (bin.equals(D)) {
			return 'd';
		} else if (bin.equals(E)) {
			return 'e';
		} else if (bin.equals(END)) {
			return '*';
		}

		// u slučaju da nije valjan binarni zapis, vrati x (pogreška)
		return 'x';
	}
}
